package com.ruoyi.framework.aspectj;

import com.google.common.base.Charsets;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.framework.config.properties.LoginErrorProperties;
import com.ruoyi.framework.shiro.service.SysLoginService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 限制登录错误的次数
 * 小问题：如果项目重启了，或布隆过滤器清除数据了，用户可以多尝试登录一次
 * 如果布隆过滤器和 Redis 中的数据都丢了，那用户可以尝试更多次
 *
 * @author 刘逸晖
 */
@Component
@Aspect
public class LimitLoginAspect {

    /**
     * 处理登录业务
     */
    @Autowired
    private SysLoginService loginService;

    /**
     * 登录错误相关配置
     */
    @Autowired
    private LoginErrorProperties loginErrorProperties;

    /**
     * 让登录方法作为切点
     */
//    @Pointcut("execution (public * com.ruoyi.framework.shiro.service.SysLoginService.login(String,String))")
    //用业务层的登录方法作为切点，不方便向页面返回剩余尝试次数，除非再嵌套一个异常
    @Pointcut("execution (public * com.ruoyi.web.controller.system.SysLoginController.ajaxLogin(String,String,Boolean))")
    public void commonPointCut() {

    }

    /**
     * 限制尝试登录的次数
     *
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "commonPointCut()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //尝试次数是否已达上线
        this.loginService.allowLogin(proceedingJoinPoint.getArgs()[0]);

        Object returnValue = null;
        try {
            returnValue = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());

            //检查是否登录失败，如果是则失败次数+1
            if (returnValue instanceof AjaxResult) {
                AjaxResult ajaxResult = (AjaxResult) returnValue;

                if (ajaxResult.isError()) {
                    //登录错误数+1
                    Integer errorCount = this.loginService.countError(proceedingJoinPoint.getArgs()[0]);

                    //响应消息中提示剩余次数
                    int remainingCount = this.loginErrorProperties.getMaxCount() - errorCount;

                    if (remainingCount<0){
                        remainingCount=0;
                    }

                    StringBuilder newMsg = new StringBuilder();

                    if (ajaxResult.get(AjaxResult.MSG_TAG) instanceof String) {//响应消息是 String 类型
                        //新消息包含旧消息
                        newMsg.append(ajaxResult.get(AjaxResult.MSG_TAG)).append(",");
                    }

                    newMsg.append("您还可以尝试").append(remainingCount).append("次");
                    ajaxResult.put(AjaxResult.MSG_TAG, newMsg);
                }
            }

        } catch (Throwable e) {
            throw e;
        }
        return returnValue;
    }

}
